﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Core.Models;
using System.Data.Objects.DataClasses;
using System.Data.Objects;
using System.Linq.Expressions;
using System.Data;

namespace Core.Repositories
{
    /// <summary>
    /// Defines a class for generic entity repository to access common functionality for data access layer.
    /// </summary>
    /// <typeparam name="TType">Type of entity</typeparam>
    /// <typeparam name="TContext">Type of object context</typeparam>
    public abstract class Repository<TType, TContext> : IRepository<TType> 
        where TContext : ObjectContext, new()
        where TType : EntityObject
    {
        private readonly ObjectContext _db;

        public Repository()
        {
            try
            {
                _db = new DatabaseEntities();
            }
            catch (Exception ex)
            {
                throw new ArgumentException(ex.Message);
            }            
        }

        public Repository(ObjectContext context)
        {
            try
            {
                _db = context;
            }
            catch (Exception ex)
            {
                throw new ArgumentException(ex.Message);
            }
        }   

        public virtual IQueryable<TType> GetAll()
        {
            try
            {
                return this._db.CreateObjectSet<TType>().AsQueryable();
            }
            catch (Exception ex)
            {
                throw new ArgumentException(ex.Message);
            }             
        }

        public virtual TType GetOne(Expression<Func<TType, bool>> filter)
        {
            try
            {
                return GetAll().SingleOrDefault(filter);
            }
            catch (Exception ex)
            {
                throw new ArgumentException(ex.Message);
            }
        }

        public virtual IQueryable<TType> GetMany(Expression<Func<TType, bool>> filter)
        {
            try
            {
                return GetAll().Where(filter);
            }
            catch (Exception ex)
            {
                throw new ArgumentException(ex.Message);
            }
        }

        public virtual bool Delete(TType entity)
        {
            try
            {                
                this._db.DeleteObject(entity);                
                this._db.SaveChanges();
                return true;
            }
            catch (Exception ex)
            {
                throw new ArgumentException(ex.Message);
            }            
        }

        public virtual bool Delete(Expression<Func<TType, bool>> filter)
        {
            try
            {
                var entity = GetOne(filter);
                if (entity == null)
                {
                    return false; // not found; assume already deleted.
                }
                return Delete(entity);
            }
            catch (Exception ex)
            {
                throw new ArgumentException(ex.Message);
            }
        }

        public virtual TType Create(TType entity)
        {
            try
            {
                this._db.CreateObjectSet<TType>().AddObject(entity);
                this._db.SaveChanges();
                return entity;
            }
            catch (Exception ex)
            {
                throw new ArgumentException(ex.Message);
            }    
        }

        public virtual bool Update(TType entity)
        {
            try
            {
                this._db.SaveChanges();
                return true;
            }
            catch (Exception ex)
            {
                throw new ArgumentException(ex.Message);
            }    
        }
    }
}
